package org.ccay.validator.validators;

import org.ccay.validator.IValidatorContext;
import org.ccay.validator.ValidationMessage;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.ValidatorContext;

/**
 *
 * 列表验证器
 * 当需要验证的对象为List、对象数组类型时，可使用此验证器
 * <!-- 片段开始: java文档 -->
 * 此验证器允许你使用对象属性自己的验证文件来验证。
 * 这样，你可以使用模型驱动的开发模式，在模块一个地方，统一管理你的验证文件。
 * 这会让你可以在多个模块重用这一验证文件。
 * 
 * 此验证器可以处理的类型有：对象、集合类型、数组。
 * <!-- 片段结束: java文档 -->
 * <p/>
 *
 * <!-- 片段开始: 参数 -->
 * <ul>
 * <li>fieldName - 如果是普通的validator标签，需要使用的字段名；如果是field-validator标签，则不需要</li>
 * </ul>
 * <!-- 片段结束: 参数 -->
 *
 * <pre>
 * <!-- 片段开始: 示例 -->
 *    &lt;method&gt;
 *		&lt;arg index="0" name="listBean"&gt;
 *        &lt;!-- 普通验证器语法 --&gt;
 *        &lt;validator type="visitor"&gt;
 *            &lt;param name="fieldName"&gt;user&lt;/param&gt;
 *            &lt;message key="error.visitor"&gt;Test exception occured.&lt;/message&gt;
 *        &lt;/validator&gt;
 *
 *        &lt;!-- 字段验证器语法 --&gt;
 *        &lt;field name="user"&gt;
 *           &lt;field-validator type="visitor"&gt;
 *            &lt;message key="error.visitor"&gt;Test exception occured.&lt;/message&gt;
 *           &lt;/field-validator&gt;
 *        &lt;/field&gt;
 *		&lt;/arg&gt;
 *    &lt;/method&gt;
 * <!-- 片段结束: 示例 -->
 * </pre>
 *
 * <!-- 片段开始: 说明 -->
 * <p>在上面的示例中，如果参数的getUser()方法返回User对象，
 * 验证框架会查找配置文件User-validation.xml来验证。
 * 并且每个属性名的前面会加上user的前缀。
 * 例如，属性名为name时，加上后就是'user.name'
 * </p>
 * <!-- 片段结束: 说明 -->
 * 
 * 
 *
 */
public class VisitorValidator extends VisitorFieldValidator {
	
	/**
	 * 参数名称
	 */
	private String argName;
	
	public String getArgName() {
		return argName;
	}

	public void setArgName(String argName) {
		this.argName = argName;
	}

	/**
	 * 使用element的元素名称
	 */
    private String elementName;

	public String getElementName() {
		return elementName;
	}

	public void setElementName(String elementName) {
		this.elementName = elementName;
	}

	/**
	 * 为空时，是否抛出异常
	 */
    private boolean errorWhenNull = true;
    
	public boolean isErrorWhenNull() {
		return errorWhenNull;
	}

	public void setErrorWhenNull(boolean isErrorWhenNull) {
		this.errorWhenNull = isErrorWhenNull;
	}
    
	/* （非 Javadoc）
	 * @see com.opensymphony.xwork2.validator.validators.VisitorFieldValidator#validate(java.lang.Object)
	 */
	@Override
	public void validate(Object object) throws ValidationException {
        String fieldName = getFieldName();
		Object value = getFieldValue(fieldName, object);
		
		String arg = getArgName();
		// 参数验证的情况
		if (null != arg && !"".equals(arg)) {
			fieldName = arg;
			value = object;
		}
		
		// 如果对象为空
        if (value == null) {
        	if (errorWhenNull) {
            	addActionError("");//TODO message key: must not to be null.
        	}
            return;
        }
        ValueStack stack = ActionContext.getContext().getValueStack();

        stack.push(object);

        validateElement(fieldName, value, super.getContext());

        stack.pop();
	}

    /**
     * @param fieldName
     * @param o
     * @param visitorContext
     * @throws ValidationException
     */
	protected void validateObject(String fieldName, Object o, String visitorContext) throws ValidationException {
        ValueStack stack = ActionContext.getContext().getValueStack();
        stack.push(o);

        ValidatorContext parent = getValidatorContext();
        ValidatorContext validatorContext = new AppendingContext(parent, o, fieldName, getMessage(o));

//      boolean appendPrefix = isAppendPrefix();
//		if (appendPrefix) {
//			validatorContext = new AppendingContext(parent, o, fieldName, getMessage(o));
//		} else {
//			validatorContext = new DelegatingValidatorContext(parent, DelegatingValidatorContext.makeTextProvider(o, parent), parent);
//		}
        
		Object action = o;
		String tagName = null;
		if (parent instanceof IValidatorContext) {
			IValidatorContext ctx = (IValidatorContext)parent;
	    	// 设置验证目标对象
			ctx.setParam("validatorTarget", o);
	    	// 设置业务对象
			action = ctx.getParam("serviceClass", Object.class);
			tagName = ctx.getParam("tagName", String.class);
	    	// 设置解析节点名称
			ctx.setParam("tagName", "element");
	    	// 设置节点名称（匹配element标签）
			ctx.setParam("elementName", elementName);
		}
		
		try {
			actionValidatorManager.validate(action, visitorContext, validatorContext);
		} finally {
			if (parent instanceof IValidatorContext) {
				// 还原解析节点
				IValidatorContext ctx = (IValidatorContext)parent;
		    	// 设置解析节点名称
				ctx.setParam("tagName", tagName);
			}
		}

        stack.pop();
    }
    public class AppendingContext extends AppendingValidatorContext implements IValidatorContext {
    	
    	IValidatorContext ctx;

        public AppendingContext(ValidatorContext parent, Object object, String field, String message) {
            super(parent, object, field, message);
            if (parent instanceof IValidatorContext) {
            	this.ctx = (IValidatorContext)parent;
            }
        }

		public <T> T getParam(String key, Class<T> requiredType) {
			return ctx.getParam(key, requiredType);
		}

		public void setParam(String key, Object value) {
			ctx.setParam(key, value);
		}

		public void addActionError(ValidationMessage message) {
			ctx.addActionError(message);
		}

		public void addFieldError(ValidationMessage message) {
			ctx.addFieldError(message);
		}
    }

}
